// 模拟交易所
// 撮合成交
use anyhow::{Result, Error};
use chrono::naive::NaiveTime;
use serde::{Serialize, Deserialize};
use crate::strategy::{Order, Strategy, Direction};
use csv;
use std::collections::HashMap;
use std::cmp::min;

pub struct MockExchange {
    ticks: Vec<Tick>,
    transactions: Vec<Transaction>,
    orders: HashMap<String, Order>,
    this_tick: Option<Tick>,
}

impl MockExchange {
    // 加载数据
    fn load(tick_path: &str, transaction_path: &str) -> Result<MockExchange> {
        let mut tick_reader = csv::Reader::from_path(tick_path).unwrap();
        let mut ticks = vec![];
        for result in tick_reader.deserialize() {
            let record: Tick = result.unwrap();
            ticks.push(record);
        }
        let mut trans_reader = csv::Reader::from_path(transaction_path).unwrap();
        let mut transactions = vec![];
        for result in trans_reader.deserialize() {
            let record: Transaction = result.unwrap();
            transactions.push(record);
        }
        let orders = HashMap::new();
        return Ok(MockExchange {
            ticks,
            transactions,
            orders,
            this_tick: None,
        });
    }

    // 回放数据
    fn play(&self) {
        // play t 准备 t+1
        // 传递tick给策略
        // 接受策略的订单
        // 处理订单
        // play transaction between t and t+1
        // 处理规则
    }

    //添加策略
    fn register_strategy(&self, strategy: Strategy) {}

    //接受报单
    fn on_order(&self, order: Order) -> OrderStatus {
        // 1. 如果是市价单，按照订单簿挂单,汇总加权价格
        // 2. 如果是限价单，排在其他订单后面，推导transaction订单是否把前面的订单撮合，然后撮合最近的。
        // 3. 如果是限价单，当对手盘价格移动到该价位时，也默认成交
        match &self.this_tick {
            None => { OrderStatus::Fail }
            Some(tick) => {
                //1. 未成交的数量
                let mut undeal_volume = order.volume;
                let mut order_deal_sum_price = 0.0;
                let mut order_deal_volume = 0;
                match &order.direction {
                    Direction::Buy => {
                        for tick_index in 1..10{
                            let tick_price = tick.get_price(tick_index).unwrap();
                            //价格超出，跳出循环
                            if tick_price>order.price{
                                break
                            }
                            let tick_volume = tick.get_volume(tick_index).unwrap();
                            let tick_deal_volume = min(tick_volume, undeal_volume);
                            order_deal_volume += tick_deal_volume;
                            order_deal_sum_price += tick_price*tick_deal_volume;
                            undeal_volume -= tick_deal_volume;
                            // 全部成交后也跳出循环
                            if undeal_volume<=0{
                                break
                            }
                        }

                        //平均成交价格
                        let deal_mean_price = order_deal_sum_price/(order_deal_volume as f64);
                    }

                    Direction::Sell => {}
                }
            }
        }
    }

    //撮合成交
    fn match_making(&self, order: Order, tick: Tick) {
        let mut left_volume = order.volume;
        match &order.direction {
            Direction::Buy => {
                let mut price = order.price;
                if price > tick.n_ask_price1 {
                    deal_volume = min(tick.n_ask_volume1, left_volume);
                    left_volume = left_volume - deal_volume;
                    if left_volume == 0 {}
                }
            }
            Direction::Sell => {}
        }
    }
}

pub enum OrderStatus {
    //报单失败
    Fail,
    //报单成功
    Success,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Tick {
    #[serde(rename = "chWindCode")]
    pub ch_wind_code: String,
    #[serde(rename = "nTime", with = "date_format")]
    pub n_time: NaiveTime,
    #[serde(rename = "Status")]
    pub status: i32,
    #[serde(rename = "PreClose", with = "float_format")]
    pub pre_close: f64,
    #[serde(rename = "Open", with = "float_format")]
    pub open: f64,
    #[serde(rename = "High", with = "float_format")]
    pub high: f64,
    #[serde(rename = "Low", with = "float_format")]
    pub low: f64,
    #[serde(rename = "nPrice", with = "float_format")]
    pub n_price: f64,
    #[serde(rename = "nAskPrice1", with = "float_format")]
    pub n_ask_price1: f64,
    #[serde(rename = "nAskPrice2", with = "float_format")]
    pub n_ask_price2: f64,
    #[serde(rename = "nAskPrice3", with = "float_format")]
    pub n_ask_price3: f64,
    #[serde(rename = "nAskPrice4", with = "float_format")]
    pub n_ask_price4: f64,
    #[serde(rename = "nAskPrice5", with = "float_format")]
    pub n_ask_price5: f64,
    #[serde(rename = "nAskPrice6", with = "float_format")]
    pub n_ask_price6: f64,
    #[serde(rename = "nAskPrice7", with = "float_format")]
    pub n_ask_price7: f64,
    #[serde(rename = "nAskPrice8", with = "float_format")]
    pub n_ask_price8: f64,
    #[serde(rename = "nAskPrice9", with = "float_format")]
    pub n_ask_price9: f64,
    #[serde(rename = "nAskPrice10", with = "float_format")]
    pub n_ask_price10: f64,
    #[serde(rename = "nAskVolume1")]
    pub n_ask_volume1: i64,
    #[serde(rename = "nAskVolume2")]
    pub n_ask_volume2: i64,
    #[serde(rename = "nAskVolume3")]
    pub n_ask_volume3: i64,
    #[serde(rename = "nAskVolume4")]
    pub n_ask_volume4: i64,
    #[serde(rename = "nAskVolume5")]
    pub n_ask_volume5: i64,
    #[serde(rename = "nAskVolume6")]
    pub n_ask_volume6: i64,
    #[serde(rename = "nAskVolume7")]
    pub n_ask_volume7: i64,
    #[serde(rename = "nAskVolume8")]
    pub n_ask_volume8: i64,
    #[serde(rename = "nAskVolume9")]
    pub n_ask_volume9: i64,
    #[serde(rename = "nAskVolume10")]
    pub n_ask_volume10: i64,
    #[serde(rename = "nBidPrice1", with = "float_format")]
    pub n_bid_price1: f64,
    #[serde(rename = "nBidPrice2", with = "float_format")]
    pub n_bid_price2: f64,
    #[serde(rename = "nBidPrice3", with = "float_format")]
    pub n_bid_price3: f64,
    #[serde(rename = "nBidPrice4", with = "float_format")]
    pub n_bid_price4: f64,
    #[serde(rename = "nBidPrice5", with = "float_format")]
    pub n_bid_price5: f64,
    #[serde(rename = "nBidPrice6", with = "float_format")]
    pub n_bid_price6: f64,
    #[serde(rename = "nBidPrice7", with = "float_format")]
    pub n_bid_price7: f64,
    #[serde(rename = "nBidPrice8", with = "float_format")]
    pub n_bid_price8: f64,
    #[serde(rename = "nBidPrice9", with = "float_format")]
    pub n_bid_price9: f64,
    #[serde(rename = "nBidPrice10", with = "float_format")]
    pub n_bid_price10: f64,
    #[serde(rename = "nBidVolume1")]
    pub n_bid_volume1: i64,
    #[serde(rename = "nBidVolume2")]
    pub n_bid_volume2: i64,
    #[serde(rename = "nBidVolume3")]
    pub n_bid_volume3: i64,
    #[serde(rename = "nBidVolume4")]
    pub n_bid_volume4: i64,
    #[serde(rename = "nBidVolume5")]
    pub n_bid_volume5: i64,
    #[serde(rename = "nBidVolume6")]
    pub n_bid_volume6: i64,
    #[serde(rename = "nBidVolume7")]
    pub n_bid_volume7: i64,
    #[serde(rename = "nBidVolume8")]
    pub n_bid_volume8: i64,
    #[serde(rename = "nBidVolume9")]
    pub n_bid_volume9: i64,
    #[serde(rename = "nBidVolume10")]
    pub n_bid_volume10: i64,
    #[serde(rename = "nMatchItems")]
    pub n_match_items: i64,
    #[serde(rename = "TotalVolume")]
    pub total_volume: i64,
    #[serde(rename = "TotalTurnover")]
    pub total_turnover: i64,
    #[serde(rename = "TotalBidVolume")]
    pub total_bid_volume: i64,
    #[serde(rename = "TotalAskVolume")]
    pub total_ask_volume: i64,
    //todo 除100处理
    #[serde(rename = "WeightedAvgBidPrice")]
    pub weighted_avg_bid_price: f64,
    //todo 除100处理
    #[serde(rename = "WeightedAvgAskPrice")]
    pub weighted_avg_ask_price: f64,
    #[serde(rename = "IOPV")]
    pub ipov: f64,
    #[serde(rename = "YieldToMaturity")]
    pub yield_to_maturity: f64,
    //todo 除100处理
    #[serde(rename = "HighLimited")]
    pub high_limited: f64,
    //todo 除100处理
    #[serde(rename = "LowLimited")]
    pub low_limited: f64,
}

//获取tick的数据宏
impl Tick {
    //todo 用宏替代
    fn get_price(&self, index: i32) -> Result<f64> {
        match index {
            1 => Ok(self.n_bid_price1),
            2 => Ok(self.n_bid_price2),
            3 => Ok(self.n_bid_price3),
            4 => Ok(self.n_bid_price4),
            5 => Ok(self.n_bid_price5),
            6 => Ok(self.n_bid_price6),
            7 => Ok(self.n_bid_price7),
            8 => Ok(self.n_bid_price8),
            9 => Ok(self.n_bid_price9),
            10 => Ok(self.n_bid_price10),
            -1 => Ok(self.n_ask_price1),
            -2 => Ok(self.n_ask_price2),
            -3 => Ok(self.n_ask_price3),
            -4 => Ok(self.n_ask_price4),
            -5 => Ok(self.n_ask_price5),
            -6 => Ok(self.n_ask_price6),
            -7 => Ok(self.n_ask_price7),
            -8 => Ok(self.n_ask_price8),
            -9 => Ok(self.n_ask_price9),
            -10 => Ok(self.n_ask_price10),
            //todo use error
            _ => panic!("wrong index")
        }
    }
    //todo 用宏替代
    fn get_volume(&self, index: i32) -> Result<i64> {
        match index {
            1 => Ok(self.n_bid_volume1),
            2 => Ok(self.n_bid_volume2),
            3 => Ok(self.n_bid_volume3),
            4 => Ok(self.n_bid_volume4),
            5 => Ok(self.n_bid_volume5),
            6 => Ok(self.n_bid_volume6),
            7 => Ok(self.n_bid_volume7),
            8 => Ok(self.n_bid_volume8),
            9 => Ok(self.n_bid_volume9),
            10 => Ok(self.n_bid_volume10),
            -1 => Ok(self.n_ask_volume1),
            -2 => Ok(self.n_ask_volume2),
            -3 => Ok(self.n_ask_volume3),
            -4 => Ok(self.n_ask_volume4),
            -5 => Ok(self.n_ask_volume5),
            -6 => Ok(self.n_ask_volume6),
            -7 => Ok(self.n_ask_volume7),
            -8 => Ok(self.n_ask_volume8),
            -9 => Ok(self.n_ask_volume9),
            -10 => Ok(self.n_ask_volume10),
            //todo use error
            _ => panic!("wrong index")
        }
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Transaction {
    #[serde(rename = "Tkr")]
    tkr: String,
    #[serde(rename = "Time", with = "date_format")]
    time: NaiveTime,
    #[serde(rename = "Index")]
    index: u64,
    #[serde(rename = "Price", with = "float_format")]
    price: f64,
    #[serde(rename = "Volume")]
    volume: i64,
    #[serde(rename = "Turnover")]
    turnover: f64,
    #[serde(rename = "BSFlag")]
    bs_flag: String,
    #[serde(rename = "OrderKind")]
    order_kind: String,
    #[serde(rename = "FunctionCode")]
    function_code: String,
    #[serde(rename = "AskOrder")]
    ask_order: u64,
    #[serde(rename = "BidOrder")]
    bid_order: u64,
}

// 处理时间
mod date_format {
    use chrono::{NaiveTime};
    use serde::{self, Deserialize, Serializer, Deserializer};
    use std::str::FromStr;

    const FORMAT: &'static str = "%H%M%S%3f";

    pub fn serialize<S>(
        date: &NaiveTime,
        serializer: S,
    ) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
    {
        let s = format!("{}", date.format(FORMAT));
        serializer.serialize_str(&s)
    }

    pub fn deserialize<'de, D>(
        deserializer: D,
    ) -> Result<NaiveTime, D::Error>
        where
            D: Deserializer<'de>,
    {
        //todo wrap error
        let s = String::deserialize(deserializer).unwrap();
        let s_len = s.len();
        let hour = u32::from_str(&s[0..s_len - 7]).unwrap();
        let minute = u32::from_str(&s[s_len - 7..s_len - 5]).unwrap();
        let second = u32::from_str(&s[s_len - 5..s_len - 3]).unwrap();
        let milli = u32::from_str(&s[s_len - 3..]).unwrap();
        let m = NaiveTime::from_hms_milli(hour, minute, second, milli);
        Ok(m)
        // Utc.datetime_from_str(&s, FORMAT).map_err(serde::de::Error::custom)
    }
}

mod float_format {
    use serde::{self, Deserialize, Serializer, Deserializer};

    pub fn serialize<S>(
        value: &f64,
        serializer: S,
    ) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
    {
        let s = format!("{:.0}", value * 10000.0);
        serializer.serialize_str(&s)
    }

    pub fn deserialize<'de, D>(
        deserializer: D,
    ) -> Result<f64, D::Error>
        where
            D: Deserializer<'de>,
    {
        let s = f64::deserialize(deserializer)?;
        Ok(s / 10000.0)
    }
}

#[cfg(test)]
mod test_read_data {
    use crate::mock_exchange::{Transaction, Tick};

    #[test]
    fn read_transaction() {
        let mut rd = csv::Reader::from_path("./data/601012.SH.Transaction.csv").unwrap();
        let mut i = 1;
        for result in rd.deserialize() {
            let record: Transaction = result.unwrap();
            println!("{:?}", record);
            i += 1;
            //打印10行
            if i >= 10 {
                break;
            }
        }
    }

    #[test]
    fn read_tick() {
        let mut rd = csv::Reader::from_path("./data/601012.SH.Tick.csv").unwrap();
        let mut i = 1;
        for result in rd.deserialize() {
            // Notice that we need to provide a type hint for automatic
            // deserialization.
            let record: Tick = result.unwrap();
            println!("{:?}", record);
            i += 1;
            //打印10行
            if i >= 10 {
                break;
            }
        }
    }
}